﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Threading;
using System.Windows.Forms;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Drawing.Drawing2D;
using System.Text;
using System.Drawing.Imaging;
using System.Xml;
using System.IO;
using System.Data.SqlClient;


namespace IWebApp2
{
    public partial class IMForm : System.Web.UI.Page
    {
        #region variables
        //roster object
        public static jabber.client.RosterManager roster;

        //queue that holds user contacts
        public static Queue<ContactDetails> contactsQ;

        //queue that holds vcard info
        public static Queue<VcardInfo> vcardQ;

        //wait handle for vcard iq
        private static ManualResetEvent alldone = new ManualResetEvent(false);

        //data table used for GridView (GridView1), contains list of contacts
        private static DataTable dt;

        //data row objects 
        //dr for contact names
        private static DataRow dr;
        //dr2 for status message
        private static DataRow dr2;

        //assign or get your own jabber ID and nickname
        //static, to make it accessible in other programs
        public static String myID=String.Empty;
        public static String myNickName = String.Empty;
        
        //array that holds all Jabber IDs only
        private static ArrayList arr;
        
        //used as counter for vcard
        private static int i = 0;

        #endregion

        #region Page_Load
        protected void Page_Load(object sender, EventArgs e)
        {
         
            try
            {
                if (!IsPostBack)
                {
                    //create new object instances
                    arr = new ArrayList();
                    contactsQ = new Queue<ContactDetails>();
                    vcardQ = new Queue<VcardInfo>();
                    roster = new jabber.client.RosterManager();
                    roster.Stream = IWebApp2.SignIn.jabberClient1;
                    dt = new DataTable();


                    //jabber event handlers
                    roster.OnRosterItem += new jabber.client.RosterItemHandler(roster_OnRosterItem);
                    roster.OnRosterEnd += new bedrock.ObjectHandler(roster_OnRosterEnd);
                    IWebApp2.SignIn.jabberClient1.OnPresence += new jabber.client.PresenceHandler(jabberClient1_OnPresence);

                    //create 3 DataColumns for GridView1
                    DataColumn[] keys = new DataColumn[3];
                    DataColumn column;
                    // Create column 1.
                    column = new DataColumn();
                    column.DataType = System.Type.GetType("System.String");
                    column.ColumnName = "Status";
                    // Add the column to the DataTable.Columns collection.
                    dt.Columns.Add(column);

                    column = new DataColumn();
                    column.DataType = System.Type.GetType("System.String");
                    column.ColumnName = "JID";
                    // Add the column to the DataTable.Columns collection.
                    dt.Columns.Add(column);

                    // Add the column to the array.
                    // Create column 2 and add it to the array.
                    column = new DataColumn();
                    column.DataType = System.Type.GetType("System.String");
                    column.ColumnName = "Contacts";
                    dt.Columns.Add(column);
                   
                    column = new DataColumn();
                    column.DataType = System.Type.GetType("System.String");
                    column.ColumnName = "Avatar";
                    dt.Columns.Add(column);
                    keys[0] = column;
                    
                    //Bind to GridView1
                    DoBinding();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + "------" + ex.StackTrace);
            }
        }
        #endregion

        #region Jabber events

        /// <summary>
        /// Roster is available
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="item">contains roster item details</param>
        protected void roster_OnRosterItem(object sender, jabber.protocol.iq.Item item)
        {
            try
            {
                if (item.Nickname != null)
                {
                    //extract content of roster item 
                    //get and save relevant informations using ContactDetails class
                    ContactDetails contact = new ContactDetails();
                    //roster jabber id
                    contact.JID = item.JID;
                    //nickname
                    contact.NickName = item.Nickname;
                    //server 
                    contact.Server = item.JID.Server;
                    //resource
                    contact.Resource = item.JID.Resource;
                    //status message, temporary data "No status message"
                    //store temporary data to avoid getting null reference exception
                    //especially when control does not accept null objects
                    contact.StatusMessage = "No status message";

                    //add to queue of UserQ
                    contactsQ.Enqueue(contact);

                    //save also copy of roster jid in array
                    arr.Add(item.JID);
                    
                }

                //store user nickname to a variable
                if (item.JID.ToString() == myID) myNickName = item.Nickname;
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }
        }

        /// <summary>
        /// Roster items complete.  
        /// </summary>
        /// <param name="sender"></param>
        protected void roster_OnRosterEnd(object sender)
        {
            //in order to retrieve Vcard
            //1. Send presence information with photo
            //2. Compose Vcard iq.
            
            
            //compose Presence using jabber xml format
            //follow standards set by XEP..(protocol) in order to retrive Vcard
            //jabber-net has no support for Avatar
            jabber.protocol.client.Presence pres = new jabber.protocol.client.Presence(IWebApp2.SignIn.jabberClient1.Document);
            jabber.protocol.Element el = new jabber.protocol.Element("x", IWebApp2.SignIn.jabberClient1.Document);
            el.SetAttribute("xmlns", "vcard-temp:x:update");
            //create an element containing photo
            jabber.protocol.Element el1 = new jabber.protocol.Element("photo", IWebApp2.SignIn.jabberClient1.Document);
            //add path where photo/ image is located
            //convert to sha1 (its important to get the hash value before sending to jabber server)
            String str2 = GetSHA1Hash("C:\\Documents and Settings\\Maricar Guingab\\My Documents\\Visual Studio 2008\\Projects\\IWebApp\\IWebApp2\\IWebApp2\\Images\\maricar-avatar.png");
            //add as text to element "photo"
            el1.InnerText = str2;
            //add all elements
            el.AddChild(el1);
            pres.AddChild(el);
            //send to jabber server
            IWebApp2.SignIn.jabberClient1.Write(pres);


            //Compose VcardIQ
            XmlDocument a = new XmlDocument();
            jabber.protocol.iq.VCardIQ contactVCard = new jabber.protocol.iq.VCardIQ(a);
            contactVCard.From = myID;
            //get user's own Vcard 
            contactVCard.To = myID;
            contactVCard.Type = jabber.protocol.client.IQType.get;
            contactVCard.ID = "vc2";
            IWebApp2.SignIn.jabberClient1.Tracker.BeginIQ(contactVCard, new jabber.connection.IqCB(VcardCallback), null);
       }

        /// <summary>
        /// Vcard Callback method
        /// </summary>
        /// <param name="o"></param>
        /// <param name="Iq"></param>
        /// <param name="p"></param>
        protected void VcardCallback(object o, jabber.protocol.client.IQ Iq, object p)
        {
            try
            {
                //create filename (fileformat--png)
                string filename = Iq.From.User + "@" + Iq.From.Server + ".png";

                //create an instance object of VcardInfo
                VcardInfo vci = new VcardInfo();

                //store id
                vci.JID = Iq.From.User + "@" + Iq.From.Server;

                //check the IQType
                if (Iq.Type == jabber.protocol.client.IQType.result)
                {

                    //cast IQ Query to Vcard
                    jabber.protocol.iq.VCard vcard = (jabber.protocol.iq.VCard)Iq.Query;
                    
                    //If Vcard contains complex name
                    jabber.protocol.iq.VCard.VName vn = vcard.ComplexName;

                    //get fullname
                    vci.FullName = vcard.FullName;

                    //get nickname
                    vci.NickName = vcard.Nickname;

                    //get node details in vn
                    foreach (System.Xml.XmlNode node in vn.ChildNodes)
                    {
                        //particularly given name, if given name is available
                        if (node.Name == "GIVEN")
                        {
                            vci.FirstName = node.InnerText;
                            if (vci.NickName == null) vci.NickName = node.InnerText;
                        }
                        //middle name, if middle name is available
                        else if (node.Name == "MIDDLE") vci.MiddleName = node.InnerText;

                        //family name, if family name is available
                        else if (node.Name == "FAMILY") vci.LastName = node.InnerText;
                    }


                    foreach (System.Xml.XmlNode node in vcard.ChildNodes)
                    {
                         //get other details, bday
                        if (node.Name == "BDAY") vci.Birthday = node.InnerText;
                        
                        //home cell
                        else if (node.Name == "HOMECELL") vci.Mobile = node.InnerText;
                        
                        //address
                        else if (node.Name == "ADR")
                        {
                            if (node.InnerText != String.Empty)
                                vci.Address = node.InnerText;
                        }
                        
                        //email
                        else if (node.Name == "EMAIL")
                        {
                            if (node.InnerText != String.Empty)
                                vci.Email = node.InnerText;
                        }
                    }

                    //get photo
                    jabber.protocol.iq.VCard.VPhoto vp = new jabber.protocol.iq.VCard.VPhoto(IWebApp2.SignIn.jabberClient1.Document);
                    vp = vcard.Photo;
                    if (vp != null)
                    {
                        //check node containing photo binval
                        foreach (System.Xml.XmlNode node in vp.ChildNodes)
                        {

                            if (node.Name == "BINVAL")
                            {
                                //if binval is available
                                try
                                {
                                    lock (this)
                                    {
                                     
                                        //get node innerText, use method Base64ToImage to convert binval to an Image
                                        System.Drawing.Image img = Base64ToImage(node.InnerText);
                                        
                                        //create thumbnail out of img
                                        System.Drawing.Image img2 = img.GetThumbnailImage(20, 20, null, IntPtr.Zero);
                                        
                                        //save to directory 
                                        img2.Save(Server.MapPath("avatar") + "\\" + filename, ImageFormat.Png);

                                        //save avatar to ContactDetails
                                        foreach (ContactDetails contact in contactsQ)
                                            if (Iq.From.User + "@" + Iq.From.Server == contact.JID) contact.Avatar = "~/avatar/" + filename;

                                        //dispose images
                                        img2.Dispose();
                                        img.Dispose();
                                    }
                                }
                                catch (Exception ex)
                                {
                                    MessageBox.Show(ex.StackTrace, ex.Message + " " + ex.Source);
                                }
                            }
                        }
                    }

                    //enqueue Vcard details in vcardQ
                    vcardQ.Enqueue(vci);
                    if (i == (arr.Count - 1))
                    {
                        //allow waiting thread to proceed
                        alldone.Set();
                    }
                    else
                    {

                        //Compose another VCardIQ that will loop within this callback, until array(arr) last index was reached
                        XmlDocument b = new XmlDocument();
                        jabber.protocol.iq.VCardIQ UserVCard2 = new jabber.protocol.iq.VCardIQ(b);
                        UserVCard2.From = myID;
                        //get contacts vcard
                        //get IDs in array(arr), using i as index
                        UserVCard2.To = arr[i].ToString();
                        UserVCard2.Type = jabber.protocol.client.IQType.get;
                        UserVCard2.ID = "vc2";
                        IWebApp2.SignIn.jabberClient1.Tracker.BeginIQ(UserVCard2, new jabber.connection.IqCB(VcardCallback), null);
                        i++;
                    }
                }

            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message + "----" + e.StackTrace);
            }
        }

        /// <summary>
        /// jabber server sent Presence Updates
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="pres"></param>
        protected void jabberClient1_OnPresence(object sender, jabber.protocol.client.Presence pres)
        {
            if (pres.From.User != null)
            {
                //if presence is available loop in contactsQ
                foreach (ContactDetails contact in contactsQ)
                {
                    //check if JIDs are the same
                    if (contact.JID == (pres.From.User.Trim() + "@" + pres.From.Server.Trim()).Trim())
                    {
                        //if current presence belongs to a contact person in contactsQ
                        //identify presence type
                        switch (pres.Type)
                        {
                            
                            //if available
                            case jabber.protocol.client.PresenceType.available:
                                {
                                    contact.Status = "~/Images/online.png";
                                    if (pres.Status == null) contact.StatusMessage = "No status message";
                                    else contact.StatusMessage = pres.Status;
                                    break;
                                }
                            //if unavailable
                            case jabber.protocol.client.PresenceType.unavailable:
                                {
                                    contact.Status = "~/Images/offline.png";
                                    if (pres.Status == null) contact.StatusMessage = "No status message";
                                    else contact.StatusMessage = pres.Status;
                                    break;
                                }
                            //if invisible
                            case jabber.protocol.client.PresenceType.invisible:
                                {
                                    contact.Status = "~/Images/idle.png";
                                    if (pres.Status == null) contact.StatusMessage = "No status message";
                                    else contact.StatusMessage = pres.Status;
                                    break;
                                }
                            //if unsubscribe
                            case jabber.protocol.client.PresenceType.unsubscribe:
                                {
                                    contact.Status = "~/Images/offline.png";
                                    if (pres.Status == null) contact.StatusMessage = "No status message";
                                    else contact.StatusMessage = pres.Status;
                                    break;
                                }

                        }

                    }

                }
            }
        }

      
        #endregion

        #region Data Binding

        /// <summary>
        /// Bind contact informations to GridView
        /// </summary>
        protected void DoBinding()
        {
            
            foreach (ContactDetails contact in contactsQ)
            {
                lock (this)
                {
                    //bind the following data to gridview
                    GridView1.DataSource = Contacts(contact.NickName, contact.JID, contact.Status, contact.StatusMessage, contact.Avatar);
                    GridView1.DataBind();
                }
            }
        }

        protected DataTable Contacts(String contact, String jid, String image, String statmessage, String avatar)
        {
            //check str if null
            if (contact != null)
            {
                //create new data row
                dr = dt.NewRow();
                //store image to Status data column
                dr["Status"] = image;
                //store jid to JID data column, this data column is hidden
                dr["JID"] = jid;
                //store contacts to Contacts data column
                dr["Contacts"] = contact;
                //store avatar to Avatar data column
                dr["Avatar"] = avatar;


                if (statmessage == String.Empty)
                {
                    //create another data row 
                    dr2 = dt.NewRow();
                    //set to no status message if statmessage is empty
                    dr2["Contacts"] = "No status message";
                }
                else
                {
                    //if there's statmessage, truncate it to less that 20 characters
                    dr2 = dt.NewRow();
                    if (statmessage.Length > 20)
                        //remove excess characters
                        dr2["Contacts"] = statmessage.Remove(21);
                        //store statmessage under Contacts data column
                    else dr2["Contacts"] = statmessage;
                }
                //add data row to data table
                dt.Rows.Add(dr);
                dt.Rows.Add(dr2);

            }
            //return data table
            return dt;
        }

        #endregion

        #region Helper Methods
        private static System.IO.FileStream GetFileStream(string pathName)
        {
            return (new System.IO.FileStream(pathName, System.IO.FileMode.Open,
                      System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite));
        }


        public static string GetSHA1Hash(string pathName)
        {
            string strResult = "";
            string strHashData = "";

            byte[] arrbytHashValue;
            System.IO.FileStream oFileStream = null;

            System.Security.Cryptography.SHA1CryptoServiceProvider oSHA1Hasher =
                       new System.Security.Cryptography.SHA1CryptoServiceProvider();

            try
            {
                oFileStream = GetFileStream(pathName);
                arrbytHashValue = oSHA1Hasher.ComputeHash(oFileStream);
                oFileStream.Close();

                strHashData = System.BitConverter.ToString(arrbytHashValue);
                strHashData = strHashData.Replace("-", "");
                strResult = strHashData;
            }
            catch (System.Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.Message, "Error!",
                         System.Windows.Forms.MessageBoxButtons.OK,
                         System.Windows.Forms.MessageBoxIcon.Error,
                         System.Windows.Forms.MessageBoxDefaultButton.Button1);
            }

            return (strResult);
        }

        public static string ImageToBase64(System.Drawing.Image image, System.Drawing.Imaging.ImageFormat format)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                // Convert Image to byte[]
                image.Save(ms, format);
                byte[] imageBytes = ms.ToArray();

                // Convert byte[] to Base64 String
                string base64String = Convert.ToBase64String(imageBytes);
                return base64String;
            }
        }


        public static System.Drawing.Image Base64ToImage(string base64String)
        {
            System.Drawing.Image image = null;
            byte[] imageBytes = Convert.FromBase64String(base64String);

            // Convert Base64 String to byte[]
            MemoryStream ms = new MemoryStream(imageBytes, 0, imageBytes.Length);
            // Convert byte[] to Image
            ms.Write(imageBytes, 0, imageBytes.Length);
            image = System.Drawing.Image.FromStream(ms, true);
            return image;
        }

      

        #endregion

        #region PageMethods (invoked from client code)


        /// <summary>
        /// Once called in client script will display somebody's Profile
        /// </summary>
        /// <param name="jid"></param>
        [System.Web.Services.WebMethod]
        [System.Web.Script.Services.ScriptMethod]
        public static void ShowProfile(String jid)
        {
            //loop to vcardQ if parameter jid exists
            foreach (VcardInfo contact in vcardQ)
            {
              
                if (contact.JID == jid)
                {
                    //set all these data in Profile.aspx
                    IWebApp2.Profile.str1 = contact.NickName;
                    IWebApp2.Profile.str2 = contact.FullName;
                    IWebApp2.Profile.str3 = contact.FirstName;
                    IWebApp2.Profile.str4 = contact.MiddleName;
                    IWebApp2.Profile.str5 = contact.LastName;
                    IWebApp2.Profile.str6 = contact.Birthday;
                    IWebApp2.Profile.str7 = contact.Address;
                    IWebApp2.Profile.str8 = contact.Mobile;
                    IWebApp2.Profile.str9 = contact.Email;
                
                }
            }
        }

        /// <summary>
        /// Once called in client script it will send presence to jabber server
        /// </summary>
        /// <param name="pres"></param>
        [System.Web.Services.WebMethod]
        [System.Web.Script.Services.ScriptMethod]
        public static void UpdatePresence(String pres)
        {
            //create status message(temporary only)
            String status = "Test 1";
            //Compose presence then send
            if (pres == "available") IWebApp2.SignIn.jabberClient1.Presence(jabber.protocol.client.PresenceType.available, status, "Ready for action", 0);
            else if (pres == "invisible") IWebApp2.SignIn.jabberClient1.Presence(jabber.protocol.client.PresenceType.invisible, status, "Ready for action", 0);
            else if (pres == "unavailable") IWebApp2.SignIn.jabberClient1.Presence(jabber.protocol.client.PresenceType.unavailable, status, "Ready for action", 0);
        }
        #endregion

        #region Form and Controls events


        /// <summary>
        /// Closes connection to jabber server and forcing garbage collection
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void ImageButton1_Click(object sender, ImageClickEventArgs e)
        {
            //Sign Out
            IWebApp2.SignIn.jabberClient1.Close();
            dr = null;
            this.Dispose();
            GC.Collect();
            GC.WaitForPendingFinalizers();
            //Redirect to SignedOut.htm
            Response.Redirect("SignedOut.htm");
        }

        /// <summary>
        /// Timer set to 1000ms 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Timer1_Tick(object sender, EventArgs e)
        {
            //wipe all contacts in data table
            dt.Clear();
            //re-create them
            DoBinding();
        }

        /// <summary>
        /// During databinding.GridView control will allow you to perform actions 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
        {
         
            if (e.Row.RowType == DataControlRowType.DataRow)
            {
                //get the contact name of the selected row
                String name = GridView1.DataKeys[e.Row.RowIndex].Value.ToString();      //if you want to get their names       
                
                //get jid of selected row
                String jid = GridView1.DataKeys[e.Row.RowIndex].Values[1].ToString();     //if you want to get their jid
                if (jid != String.Empty)
                {
                    if (e.Row.RowState == DataControlRowState.Alternate)
                    {
                        //on mouseover change color to orange
                        e.Row.Attributes.Add("onmouseover", "this.style.color='orange';");
                        //on mouse out change color
                        e.Row.Attributes.Add("onmouseout", "this.style.color='#444957';");
                        //on contextmenu call client script
                        e.Row.Attributes.Add("oncontextmenu", "javascript:return oCustomContextMenu.Display(event);");
                    }
                    else
                    {
                       
                        //add style, font size to 12px
                        e.Row.Style.Add("font-size", "12px");
                        //font-weight to "Bold"
                        e.Row.Style.Add("font-weight", "Bold");
                        //on mouseover and mouse out change color
                        e.Row.Attributes.Add("onmouseover", "this.style.color='orange';");
                        e.Row.Attributes.Add("onmouseout", "this.style.color='#444957';");
                        //on contextmenu call client script
                        e.Row.Attributes.Add("oncontextmenu", "javascript:return oCustomContextMenu.Display(event,'" + jid + "')");
                        //on click call client script 
                        e.Row.Attributes.Add("onclick", "callFunctions('" + name + "')");     //this is how to pass string to javascript
                    }
                }
            }

        }

        /// <summary>
        /// Upon GridView row creation set jid colummn to invisible
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void GridView1_RowCreated(object sender, GridViewRowEventArgs e)
        {
            e.Row.Cells[1].Visible = false;
        }

        #endregion
    }

}

